home *** CD-ROM | disk | FTP | other *** search
/ 3D Game Programming All in One / 3D Game Programming All in One Disc.iso / 3D2E / RESOURCES / CH20 / creator / debugger / debugger.cs next >
Text File  |  2005-11-23  |  15KB  |  553 lines

  1. //-----------------------------------------------------------------------------
  2. // Torque Game Engine
  3. // Copyright (C) GarageGames.com, Inc.
  4. //-----------------------------------------------------------------------------
  5.  
  6.  
  7. //-----------------------------------------------------------------------------
  8. // Remote scripting debugger
  9.  
  10. // To use the debugger, use dbgSetParameters(port, password); in the console
  11. // of the server to enable debugger connections.  Then on some other system,
  12. // start up the app (you don't have to start a game or connect to the
  13. // server) and exec("common/debugger/debugger.cs"); in the console.  Then use
  14. // the debugger GUI to connect to the server with the right port and password.
  15.  
  16.  
  17.  
  18. // Create the GUIs.
  19. exec("./DebuggerBreakConditionDlg.gui");
  20. exec("./DebuggerConnectDlg.gui");
  21. exec("./DebuggerEditWatchDlg.gui");
  22. exec("./DebuggerFindDlg.gui");
  23. exec("./DebuggerGui.gui");
  24. exec("./DebuggerWatchDlg.gui");
  25.  
  26. // Create a TCP object named TCPDebugger.
  27. new TCPObject(TCPDebugger);
  28.  
  29.  
  30. // Used to get unique IDs for breakpoints and watch expressions.
  31. $DbgBreakId = 0;
  32. $DbgWatchSeq = 1;
  33.  
  34.  
  35. // Functions for the TCPDebugger object:
  36.  
  37. // onLine is invoked whenever the TCP object receives a line from the server.
  38. // Treat the first word as a "command" and dispatch to an appropriate
  39. // handler.
  40. function TCPDebugger::onLine(%this, %line)
  41. {
  42.    echo("Got line=>" @ %line);
  43.    %cmd = firstWord(%line);
  44.    %rest = restWords(%line);
  45.    
  46.    if (%cmd $= "PASS") {
  47.       %this.handlePass(%rest);
  48.    }
  49.    else if(%cmd $= "COUT") {
  50.       %this.handleLineOut(%rest);
  51.    }
  52.    else if(%cmd $= "FILELISTOUT") {
  53.       %this.handleFileList(%rest);
  54.    }
  55.    else if(%cmd $= "BREAKLISTOUT") {
  56.       %this.handleBreakList(%rest);
  57.    }
  58.    else if(%cmd $= "BREAK") {
  59.       %this.handleBreak(%rest);
  60.    }
  61.    else if(%cmd $= "RUNNING") {
  62.       %this.handleRunning();
  63.    }
  64.    else if(%cmd $= "EVALOUT") {
  65.       %this.handleEvalOut(%rest);
  66.    }
  67.    else {
  68.       %this.handleError(%line);
  69.    }
  70. }
  71.  
  72. // Handler for PASS response.
  73. function TCPDebugger::handlePass(%this, %message)
  74. {
  75.    if (%message $= "WrongPass") {
  76.       DebuggerConsoleView.print("Disconnected - wrong password.");   
  77.       %this.disconnect();
  78.    }
  79.    else if(%message $= "Connected.") {
  80.       DebuggerConsoleView.print("Connected.");
  81.       DebuggerStatus.setValue("CONNECTED");
  82.       %this.send("FILELIST\r\n");
  83.    }
  84. }
  85.  
  86. // Handler for COUT response.
  87. function TCPDebugger::handleLineOut(%this, %line)
  88. {
  89.    DebuggerConsoleView.print(%line);
  90. }
  91.  
  92. // Handler for FILELISTOUT response.
  93. function TCPDebugger::handleFileList(%this, %line)
  94. {
  95.    DebuggerFilePopup.clear();
  96.    %word = 0;
  97.    while ((%file = getWord(%line, %word)) !$= "") {
  98.       %word++;
  99.       DebuggerFilePopup.add(%file, %word);
  100.    }
  101. }
  102.  
  103. // Handler for BREAKLISTOUT response.
  104. function TCPDebugger::handleBreakList(%this, %line)
  105. {
  106.    %file = getWord(%line, 0);
  107.    if (%file != $DebuggerFile) {
  108.       return;
  109.    }
  110.    %pairs = getWord(%line, 1);
  111.    %curLine = 1;
  112.    DebuggerFileView.clearBreakPositions();
  113.    
  114.    // Set the possible break positions.
  115.    for (%i = 0; %i < %pairs; %i++) {
  116.       %skip = getWord(%line, %i * 2 + 2);
  117.       %breaks = getWord(%line, %i * 2 + 3);
  118.       %curLine += %skip;
  119.       for (%j = 0; %j < %breaks; %j++) {
  120.          DebuggerFileView.setBreakPosition(%curLine);
  121.          %curLine++;
  122.       }
  123.    }
  124.  
  125.    // Now set the actual break points.
  126.    for (%i = 0; %i < DebuggerBreakPoints.rowCount(); %i++) {
  127.       %breakText = DebuggerBreakPoints.getRowText(%i);
  128.       %breakLine = getField(%breakText, 0);
  129.       %breakFile = getField(%breakText, 1);
  130.       if (%breakFile == $DebuggerFile) {
  131.          DebuggerFileView.setBreak(%breakLine);
  132.       }
  133.    }
  134. }
  135.  
  136. // Handler for BREAK response.
  137. function TCPDebugger::handleBreak(%this, %line)
  138. {
  139.    DebuggerStatus.setValue("BREAK");
  140.    
  141.    // Query all the watches.
  142.    for (%i = 0; %i < DebuggerWatchView.rowCount(); %i++) {
  143.       %id = DebuggerWatchView.getRowId(%i);
  144.       %row = DebuggerWatchView.getRowTextById(%id);
  145.       %expr = getField(%row, 0);
  146.       %this.send("EVAL " @ %id @ " 0 " @ %expr @ "\r\n");
  147.    }
  148.  
  149.    // Update the call stack window.
  150.    DebuggerCallStack.clear();
  151.  
  152.    %file = getWord(%line, 0);
  153.    %lineNumber = getWord(%line, 1);
  154.    %funcName = getWord(%line, 2);
  155.    
  156.    DbgOpenFile(%file, %lineNumber, true);
  157.  
  158.    %nextWord = 3;
  159.    %rowId = 0;
  160.    %id = 0;
  161.    while(1) {
  162.       DebuggerCallStack.setRowById(%id, %file @ "\t" @ %lineNumber @ "\t" @ %funcName);
  163.       %id++;
  164.       %file = getWord(%line, %nextWord);
  165.       %lineNumber = getWord(%line, %nextWord + 1);
  166.       %funcName = getWord(%line, %nextWord + 2);
  167.       %nextWord += 3;
  168.       if (%file $= "") {
  169.          break;
  170.       }
  171.    }
  172. }
  173.  
  174. // Handler for RUNNING response.
  175. function TCPDebugger::handleRunning(%this)
  176. {
  177.    DebuggerFileView.setCurrentLine(-1, true);
  178.    DebuggerCallStack.clear();
  179.    DebuggerStatus.setValue("RUNNING...");
  180. }
  181.  
  182. // Handler for EVALOUT response.
  183. function TCPDebugger::handleEvalOut(%this, %line)
  184. {
  185.    %id = firstWord(%line);
  186.    %value = restWords(%line);
  187.  
  188.    // See if it's the cursor watch, or from the watch window.
  189.    if (%id < 0) {
  190.       DebuggerCursorWatch.setText(DebuggerCursorWatch.expr SPC "=" SPC %value);
  191.    }
  192.    else {
  193.       %row = DebuggerWatchView.getRowTextById(%id);
  194.       if (%row $= "") {
  195.          return;
  196.       }
  197.       %expr = getField(%row, 0);
  198.       DebuggerWatchView.setRowById(%id, %expr @ "\t" @ %value);
  199.    }
  200. }
  201.  
  202. // Handler for unrecognized response.
  203. function TCPDebugger::handleError(%this, %line)
  204. {
  205.    DebuggerConsoleView.print("ERROR - bogus message: " @ %line);
  206. }
  207.  
  208.  
  209.  
  210. // XXX Do we want/need to define any of these?
  211.  
  212. function TCPDebugger::onDNSResolve(%this)
  213. {
  214. }
  215.  
  216. function TCPDebugger::onConnecting(%this)
  217. {
  218. }
  219.  
  220. function TCPDebugger::onConnected(%this)
  221. {
  222. }
  223.  
  224. function TCPDebugger::onConnectFailed(%this)
  225. {
  226. }
  227.  
  228. function TCPDebugger::onDisconnect(%this)
  229. {
  230. }
  231.  
  232.  
  233.  
  234. // Functions for the GUI objects:
  235.  
  236. // Print a line of response from the server.
  237. function DebuggerConsoleView::print(%this, %line)
  238. {
  239.    %row = %this.addRow(0, %line);
  240.    %this.scrollVisible(%row);
  241. }
  242.  
  243. // When entry in file list selected, open the file.
  244. function DebuggerFilePopup::onSelect(%this, %id, %text)
  245. {
  246.    DbgOpenFile(%text, 0, false);
  247. }
  248.  
  249. // When entry on call stack selected, open the file and go to the line.
  250. function DebuggerCallStack::onAction(%this)
  251. {
  252.    %id = %this.getSelectedId();
  253.    if (%id == -1) {
  254.       return;
  255.    }
  256.    %text = %this.getRowTextById(%id);
  257.    %file = getField(%text, 0);
  258.    %line = getField(%text, 1);
  259.    
  260.    DbgOpenFile(%file, %line, %id == 0);
  261. }
  262.  
  263. // Add a breakpoint at the selected spot, if it doesn't already exist.
  264. function DebuggerBreakPoints::addBreak(%this, %file, %line, %clear, %passct, %expr)
  265. {
  266.    // columns 0 = line, 1 = file, 2 = expr
  267.    %textLine = %line @ "\t" @ %file @ "\t" @ %expr @ "\t" @ %passct @ "\t" @ %clear;
  268.    %selId = %this.getSelectedId();
  269.    %selText = %this.getRowTextById(%selId);
  270.    if ((getField(%selText, 0) $= %line) && (getField(%selText, 1) $= %file)) {
  271.       %this.setRowById(%selId, %textLine);
  272.    }
  273.    else {
  274.       %this.addRow($DbgBreakId, %textLine);
  275.       $DbgBreakId++;
  276.    }
  277. }
  278.  
  279. // Remove the selected breakpoint.
  280. function DebuggerBreakPoints::removeBreak(%this, %file, %line)
  281. {
  282.    for (%i = 0; %i < %this.rowCount(); %i++) {
  283.       %id = %this.getRowId(%i);
  284.       %text = %this.getRowTextById(%id);
  285.       if ((getField(%text, 0) $= %line) && (getField(%text, 1) $= %file)) {
  286.          %this.removeRowById(%id);
  287.          return;
  288.       }
  289.    }
  290. }
  291.  
  292. // Remove all breakpoints.
  293. function DebuggerBreakPoints::clearBreaks(%this)
  294. {
  295.    while (%this.rowCount()) {
  296.       %id = %this.getRowId(0);
  297.       %text = %this.getRowTextById(%id);
  298.       %file = getField(%text, 1);
  299.       %line = getField(%text, 0);
  300.       DbgRemoveBreakPoint(%file, %line);
  301.    }
  302. }
  303.  
  304. // Go to file & line for the selected breakpoint.
  305. function DebuggerBreakPoints::onAction(%this)
  306. {
  307.    %id = %this.getSelectedId();
  308.    if (%id == -1) {
  309.       return;
  310.    }
  311.    %text = %this.getRowTextById(%id);
  312.    %line = getField(%text, 0);
  313.    %file = getField(%text, 1);
  314.    
  315.    DbgOpenFile(%file, %line, false);
  316. }
  317.  
  318. // Handle breakpoint removal executed from the file-view GUI.
  319. function DebuggerFileView::onRemoveBreakPoint(%this, %line)
  320. {
  321.    %file = $DebuggerFile;
  322.    DbgRemoveBreakPoint(%file, %line);
  323. }
  324.  
  325. // Handle breakpoint addition executed from the file-view GUI.
  326. function DebuggerFileView::onSetBreakPoint(%this, %line)
  327. {
  328.    %file = $DebuggerFile;
  329.    DbgSetBreakPoint(%file, %line, 0, 0, true);
  330. }
  331.  
  332.  
  333. // Support functions:
  334.  
  335. // Add a watch expression.
  336. function DbgWatchDialogAdd()
  337. {
  338.    %expr = WatchDialogExpression.getValue();
  339.    if (%expr !$= "") {
  340.       DebuggerWatchView.setRowById($DbgWatchSeq, %expr @"\t(unknown)");
  341.       TCPDebugger.send("EVAL " @ $DbgWatchSeq @ " 0 " @ %expr @ "\r\n");
  342.       $DbgWatchSeq++;
  343.    }
  344.    Canvas.popDialog(DebuggerWatchDlg);
  345. }
  346.  
  347. // Edit a watch expression.
  348. function DbgWatchDialogEdit()
  349. {
  350.    %newValue = EditWatchDialogValue.getValue();
  351.    %id = DebuggerWatchView.getSelectedId();
  352.    if (%id >= 0) {
  353.       %row = DebuggerWatchView.getRowTextById(%id);
  354.       %expr = getField(%row, 0);
  355.       if (%newValue $= "") {
  356.          %assignment = %expr @ " = \"\"";
  357.       }
  358.       else {
  359.          %assignment = %expr @ " = " @ %newValue;
  360.       }
  361.       TCPDebugger.send("EVAL " @ %id  @ " 0 " @ %assignment @ "\r\n");
  362.    }
  363.    Canvas.popDialog(DebuggerEditWatchDlg);
  364. }
  365.  
  366. // Set/change the singular "cursor watch" expression.
  367. function DbgSetCursorWatch(%expr)
  368. {
  369.    DebuggerCursorWatch.expr = %expr;
  370.    if (DebuggerCursorWatch.expr $= "") {
  371.       DebuggerCursorWatch.setText("");
  372.    }
  373.    else {
  374.       TCPDebugger.send("EVAL -1 0 " @ DebuggerCursorWatch.expr @ "\r\n");
  375.    }
  376. }
  377.  
  378. // Connect to the server with the given addr/port/password.
  379. function DbgConnect()
  380. {
  381.    %address = DebuggerConnectAddress.getValue();
  382.    %port = DebuggerConnectPort.getValue();
  383.    %password = DebuggerConnectPassword.getValue();
  384.  
  385.    if ((%address !$= "" ) && (%port !$= "" ) && (%password !$= "" )) {
  386.       TCPDebugger.connect(%address @ ":" @ %port);
  387.       TCPDebugger.schedule(5000, send, %password @ "\r\n");
  388.       TCPDebugger.password = %password;
  389.    }
  390.  
  391.    Canvas.popDialog(DebuggerConnectDlg);
  392. }
  393.  
  394. // Put a condition on a breakpoint.
  395. function DbgBreakConditionSet()
  396. {
  397.    // Read the condition.
  398.    %condition = BreakCondition.getValue();
  399.    %passct = BreakPassCount.getValue();
  400.    %clear = BreakClear.getValue();
  401.    if (%condition $= "") {
  402.       %condition = "true";
  403.    }
  404.    if (%passct $= "") {
  405.       %passct = "0";
  406.    }
  407.    if (%clear $= "") {
  408.       %clear = "false";
  409.    }
  410.    
  411.    // Set the condition.
  412.    %id = DebuggerBreakPoints.getSelectedId();
  413.    if (%id != -1) {
  414.       %bkp = DebuggerBreakPoints.getRowTextById(%id);
  415.       DbgSetBreakPoint(getField(%bkp, 1), getField(%bkp, 0), %clear, %passct, %condition);
  416.    }
  417.  
  418.    Canvas.popDialog(DebuggerBreakConditionDlg);
  419. }
  420.  
  421. // Open a file, go to the indicated line, and optionally select the line.
  422. function DbgOpenFile(%file, %line, %selectLine)
  423. {
  424.    if (%file !$= "") {
  425.       // Open the file in the file view.
  426.       if (DebuggerFileView.open(%file)) {
  427.          // Go to the line.
  428.          DebuggerFileView.setCurrentLine(%line, %selectLine);
  429.          // Get the breakpoints for this file.
  430.          if (%file !$= $DebuggerFile) {
  431.             TCPDebugger.send("BREAKLIST " @ %file @ "\r\n");
  432.             $DebuggerFile = %file;
  433.          }
  434.       }
  435.    }
  436. }
  437.  
  438. // Search in the fileview GUI.
  439. function DbgFileViewFind()
  440. {
  441.    %searchString = DebuggerFindStringText.getValue();
  442.    DebuggerFileView.findString(%searchString);
  443.  
  444.    Canvas.popDialog(DebuggerFindDlg);
  445. }
  446.  
  447. // Set a breakpoint, optionally with condition.
  448. function DbgSetBreakPoint(%file, %line, %clear, %passct, %expr)
  449. {
  450.    if (!%clear) {
  451.       if (%file == $DebuggerFile) {
  452.          DebuggerFileView.setBreak(%line);
  453.       }
  454.    }
  455.    DebuggerBreakPoints.addBreak(%file, %line, %clear, %passct, %expr);
  456.    TCPDebugger.send("BRKSET " @ %file @ " " @ %line @ " " @ %clear @ " " @ %passct @ " " @ %expr @ "\r\n");
  457. }
  458.  
  459. // Remove a breakpoint.
  460. function DbgRemoveBreakPoint(%file, %line)
  461. {
  462.    if (%file == $DebuggerFile) {
  463.       DebuggerFileView.removeBreak(%line);
  464.    }
  465.    TCPDebugger.send("BRKCLR " @ %file @ " " @ %line @ "\r\n");
  466.    DebuggerBreakPoints.removeBreak(%file, %line);
  467. }
  468.  
  469. // Remove whatever breakpoint is selected in the breakpoints GUI.
  470. function DbgDeleteSelectedBreak()
  471. {
  472.    %selectedBreak = DebuggerBreakPoints.getSelectedId();
  473.    %rowNum = DebuggerBreakPoints.getRowNumById(%selectedWatch);
  474.    if (%rowNum >= 0) {
  475.       %breakText = DebuggerBreakPoints.getRowText(%rowNum);
  476.       %breakLine = getField(%breakText, 0);
  477.       %breakFile = getField(%breakText, 1);
  478.       DbgRemoveBreakPoint(%breakFile, %breakLine);
  479.    }
  480. }
  481.  
  482. // Send an expression to the server for evaluation.
  483. function DbgConsoleEntryReturn()
  484. {
  485.    %msg = DbgConsoleEntry.getValue();
  486.    if (%msg !$= "") {
  487.       DebuggerConsoleView.print("%" @ %msg);
  488.       if (DebuggerStatus.getValue() $= "NOT CONNECTED") {
  489.          DebuggerConsoleView.print("*** Not connected.");
  490.       }
  491.       else if (DebuggerStatus.getValue() $= "BREAK") {
  492.          DebuggerConsoleView.print("*** Target is in BREAK mode.");
  493.       }
  494.       else {
  495.          TCPDebugger.send("CEVAL " @ %msg @ "\r\n");
  496.       }
  497.    }
  498.    DbgConsoleEntry.setValue("");
  499. }
  500.  
  501. // Print a line from the server.
  502. function DbgConsolePrint(%status)
  503. {
  504.    DebuggerConsoleView.print(%status);
  505. }
  506.  
  507. // Delete the currently selected watch expression.
  508. function DbgDeleteSelectedWatch()
  509. {
  510.    %selectedWatch = DebuggerWatchView.getSelectedId();
  511.    %rowNum = DebuggerWatchView.getRowNumById(%selectedWatch);
  512.    DebuggerWatchView.removeRow(%rowNum);
  513. }
  514.  
  515. // Evaluate all the watch expressions.
  516. function DbgRefreshWatches()
  517. {
  518.    for (%i = 0; %i < DebuggerWatchView.rowCount(); %i++) {
  519.       %id = DebuggerWatchView.getRowId(%i);
  520.       %row = DebuggerWatchView.getRowTextById(%id);
  521.       %expr = getField(%row, 0);
  522.       TCPDebugger.send("EVAL " @ %id @ " 0 " @ %expr @ "\r\n");
  523.    }
  524. }
  525.  
  526. // Various functions for doing incremental execution.
  527.  
  528. function dbgStepIn()
  529. {
  530.    TCPDebugger.send("STEPIN\r\n");
  531. }
  532.  
  533. function dbgStepOut()
  534. {
  535.    TCPDebugger.send("STEPOUT\r\n");
  536. }
  537.  
  538. function dbgStepOver()
  539. {
  540.    TCPDebugger.send("STEPOVER\r\n");
  541. }
  542.  
  543. function dbgContinue()
  544. {
  545.    TCPDebugger.send("CONTINUE\r\n");
  546. }
  547.  
  548.  
  549. // Start up the GUI.
  550. DebuggerConsoleView.setActive(false);
  551. Canvas.pushDialog(DebuggerGui);
  552.  
  553.